package jtokyotyrant;

import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;

import jtokyotyrant.protocol.Command;
import jtokyotyrant.protocol.CommandFuture;
import jtokyotyrant.protocol.Get;
import jtokyotyrant.protocol.Out;
import jtokyotyrant.protocol.Put;
import jtokyotyrant.protocol.Fwmkeys;
import jtokyotyrant.protocol.Misc;

public class MRDB {
    private RDB rdb;

    public MRDB() {
        rdb = JTT.rdbnew();
    }

    /**
     * This method is used in order to get the message string corresponding to
     * an error code.
     * @param ecode specifies the error code.
     * @return messge string of the error code.
     */
    public String errmsg(int ecode) {
        return rdb.errmsg(ecode);
    }

    /**
     * This method is used in order to get the last happened error code of a
     * remote database object.
     * <p>
     * The following error code is defined:
     * <ul>
     * <li> {@link #ESUCCESS}
     * <li> {@link #EINVALID}
     * <li> {@link #ENOHOST}
     * <li> {@link #EREFUSED}
     * <li> {@link #ESEND}
     * <li> {@link #ERECV}
     * <li> {@link #EKEEP}
     * <li> {@link #ENOREC}
     * <li> {@link #EMISC}
     * </ul>
     * </p>
     * @return last happened error code.
     */
    public int ecode() {
        return rdb.ecode();
    }

    /**
     * This method is used in order to set the tuning parameters of a hash
     * database object.
     * <p>
     * Note that the tuning parameters should be set before the database
     * is opened.
     * </p>
     * @param timeoutSec specifies the timeout of each query in seconds.
     *            If it is not more than 0, the timeout is not specified.
     * @param opts specifies options by bitwise-or: {@link #TRECON} specifies
     *            that the connection is recovered automatically when it is
     *            disconnected.
     * @return If successful, the return value is true, else, it is false.
     */
    public boolean tune(double timeoutSec, int opts) {
        return rdb.tune(timeoutSec, opts);
    }

    /**
     * This method is used in order to open a remote database.
     * @param host specifies the name or the address of the server.
     * @param port specifies the port number. If it is not more than 0,
     *            UNIX domain socket is used and the path of the socket file is
     *            specified by the host parameter.
     * @return If successful, the return value is true, else, it is false.
     */
    public boolean open(String host, int port) {
        return rdb.open(host, port);
    }

    /**
     * This method is used in order to open a remote database with a simple
     * server expression.
     * @param serverExpr specifies the simple server expression.
     *            It is composed of two substrings separated by ":".
     *            The former field specifies the name or the address of the
     *            server. The latter field specifies the port number.
     *            If the latter field is omitted, the default port number 1978
     +            is specified.
     * @return If successful, the return value is true, else, it is false.
     */
    public boolean open(String serverExpr) {
        return rdb.open(serverExpr);
    }

    /**
     * This method is s used in order to close a remote database object.
     * @return If successful, the return value is true, else, it is false.
     */
    public boolean close() {
        return rdb.close();
    }

    /**
     * This method is used in order to store a record into a remote database object.
     * <p>
     * If a record with the same key exists in the database, it is overwritten.
     * </p>
     * @param key specifies the key
     * @param value specfies the value
     * @return If successful, the return value is true, else, it is false.
     */
    public Future<Boolean> put(byte[] key, byte[] value) {
        return execute(new Put(rdb, key, value));
    }

    /**
     * This method is used in order to store a string record into a remote database object.
     * <p>
     * If a record with the same key exists in the database, it is overwritten.
     * </p>
     * @param key specifies the key
     * @param value specfies the value
     * @return If successful, the return value is true, else, it is false.
     */
    public Future<Boolean> put(String key, String value) {
        return execute(new Put(rdb, key, value));
    }

    /**
     * This method is used in order to remove a record of a remote database
     * object.
     * @param key specifies the key
     * @return If successful, the return value is true, else, it is false.
     */
    public Future<Boolean> out(byte[] key) {
        return execute(new Out(rdb, key));
    }

    /**
     * This method is used in order to remove a record of a remote database
     * object.
     * @param key specifies the key
     * @return If successful, the return value is true, else, it is false.
     */
    public Future<Boolean> out(String key) {
        return execute(new Out(rdb, key));
    }

    /**
     * This method is used in order to retrieve a record in a remote database
     * object.
     * @param key specifies the key
     * @return If successful, the return value is the value of the corresponding
     *         record. If no record corrsponds, the return value is 'null'.
     */
    public Future<Object> get(byte[] key) {
        return execute(new Get(rdb, key));
    }

    /**
     * This method is used in order to retrieve a record in a remote database
     * object.
     * @param key specifies the key
     * @return If successful, the return value is the value of the corresponding
     *         record. If no record corrsponds, the return value is 'null'.
     */
    public Future<Object> get(String key) {
        return execute(new Get(rdb, key));
    }

    /**
     * This method is used in order to get forward matching keys in a remote
     * database object.
     * @param prefix specifies the prefix.
     * @param max specifies the maximum number of keys to be fetched.
     *           If it is negative, no limit is specified.
     * @return The return value is a list of the corresponding keys.
     *         This function does never fail. It returns an empty list even if
     *         no key corresponds.
     */
    public Future<Object> fwmkeys(byte[] prefix, int max) {
        return execute(new Fwmkeys(rdb, prefix, max));
    }

    /**
     * This method is used in order to get forward matching keys in a remote
     * database object.
     * @param prefix specifies the prefix.
     * @param max specifies the maximum number of keys to be fetched.
     *           If it is negative, no limit is specified.
     * @return The return value is a list of the corresponding keys.
     *         This function does never fail. It returns an empty list even if
     *         no key corresponds.
     */
    public Future<Object> fwmkeys(String prefix, int max) {
        return execute(new Fwmkeys(rdb, prefix, max));
    }

    /**
     * @see #misc(String, int, byte[][])
     * @param funcName specifies the name of the function.
     * @param args specifies function arguments.
     * @return If successful, the return value is a list object of the result.
     *        `null' is returned on failure.
     */
    public Future<Object> misc(String funcName, byte[]... args) {
        return execute(new Misc(rdb, funcName, args));
    }

    /**
     * This method is used in order to call a versatile function for
     * miscellaneous operations of a remote database object.
     * <p>
     * The following list shows all functions supported by all databases.
     * <ul>
     * <li>"put" is to store a record. It receives a key and a value, and returns an empty list.
     * <li>"out" is to remove a record. It receives a key, and returns an empty list.
     * <li>"get" is to retrieve a record. It receives a key, and returns a list of the values.
     * <li>"putlist" is to store records. It receives keys and values one after the other, and returns an empty list.
     * <li>"outlist" is to remove records. It receives keys, and returns an empty list.
     * <li>"getlist" is to retrieve records. It receives keys, and returns keys and values of corresponding records one after the other.
     * </ul>
     * </p>
     * @param funcName specifies the name of the function.
     * @param opts specifies options by bitwise-or: {@link #MONOULOG} for
     *           omission of the update log.
     * @param args specifies function arguments.
     * @return If successful, the return value is a list object of the result.
     *        `null' is returned on failure.
     */
    public Future<Object> misc(String funcName, int opts, byte[]... args) {
        return execute(new Misc(rdb, funcName, opts, args));
    }

    /**
     * @see #misc(String, int, byte[][])
     * @param funcName specifies the name of the function.
     * @param args specifies function arguments.
     * @return If successful, the return value is a list object of the result.
     *        `null' is returned on failure.
     */
    public Future<Object> misc(String funcName, String... args) {
        return execute(new Misc(rdb, funcName, args));
    }

    /**
     * @see #misc(String, int, byte[][])
     * @param funcName specifies the name of the function.
     * @param opts specifies options by bitwise-or: {@link #MONOULOG} for
     *           omission of the update log.
     * @param args specifies function arguments.
     * @return If successful, the return value is a list object of the result.
     *        `null' is returned on failure.
     */
    public Future<Object> misc(String funcName, int opts, String... args) {
        return execute(new Misc(rdb, funcName, opts, args));
    }

    protected <T> CommandFuture<T> execute(Command<T> command) {
        CommandFuture<T> future = command.submit();
        return future;
    }
}
